package com.ai.wxy.spring.service;

import com.ai.spring.common.enums.ResponseCodeEnum;
import com.ai.spring.common.exception.ServiceException;
import com.ai.spring.common.util.ResponseResult;
import com.ai.spring.common.util.ResponseResultUtil;
import com.ai.spring.common.util.ValidationUtils;
import com.ai.wxy.spring.service.api.BaseService;
import com.ai.wxy.spring.service.api.ServiceCaller;
import com.ai.wxy.spring.service.api.dto.ServiceContext;
import com.ai.wxy.spring.service.api.dto.ServiceParam;
import com.ai.wxy.spring.service.api.dto.ServiceResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.ReflectionUtils;
import org.springframework.validation.annotation.Validated;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * 默认的服务调用，spring bean调用
 *
 * @author 石头
 * @Date 2019/11/18
 * @Version 1.0
 **/
@Slf4j
public class DefaultServiceCaller implements ServiceCaller,ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public ResponseResult<ServiceResult> excute(ServiceContext context) {
        if (context == null){
            return ResponseResultUtil.ofError(ResponseCodeEnum.PARAM_ERROR.getCode(),ResponseCodeEnum.PARAM_ERROR.getName());
        }

        String callerId  = context.getRequestId();
        String serviceId = context.getServiceId();
        String methodName= context.getMethodName();
        log.info("-----({})调用开始:{}.{}-----------------",callerId,serviceId,methodName);
        long start = System.currentTimeMillis();
        BaseService serviceRef = applicationContext.getBean(serviceId,BaseService.class);
        if (serviceRef == null){
            log.warn("-----({})调用服务不存在:{}-----------------",callerId,serviceId);
            return ResponseResultUtil.ofError(ResponseCodeEnum.SERVICE_ID_ERROR.getCode(),ResponseCodeEnum.SERVICE_ID_ERROR.getName());
        }
        Method serviceMethod = ReflectionUtils.findMethod(serviceRef.getClass(),methodName,context.getParamCls());
        if (serviceMethod == null){
            log.warn("-----({})调用服务方法不存在:{}.{}-----------------",callerId,serviceId,methodName);
            return ResponseResultUtil.ofError(ResponseCodeEnum.SERVICE_METHOD_ERROR.getCode(),ResponseCodeEnum.SERVICE_METHOD_ERROR.getName());
        }

        try{
            ResponseResult<ServiceResult> errorResult = validated(serviceMethod,context.getParamVals());
            if (errorResult!=null){
                return errorResult;
            }
            Object result = ReflectionUtils.invokeMethod(serviceMethod,serviceRef,context.getParamVals());
            if (result != null && result instanceof ServiceResult){
                return ResponseResultUtil.ofSuccess((ServiceResult)result);
            }else if (result!=null){
                return ResponseResultUtil.ofError(ResponseCodeEnum.SERVICE_RTN_TYPE_ERROR.getCode(),ResponseCodeEnum.SERVICE_RTN_TYPE_ERROR.getName());
            }
            return ResponseResultUtil.ofSuccess(null);
        }catch (ServiceException e){
            log.warn("-----({})调用服务方法业务失败:{}.{}:{}-----------------",callerId,serviceId,methodName,e.getCode());
            return ResponseResultUtil.ofError(e.getCode(),e.getMessage());
        }catch (Exception e){
            log.error("-----({"+callerId+"})调用服务方法失败:-----------------",e);
        }finally {
            long usedTime = System.currentTimeMillis() - start;
            log.info("-----({})调用结束:{}.{} 耗时:{}-----------------",callerId,serviceId,methodName,usedTime);
        }
        log.info("-----({})调用进入结束:{}.{}-----------------",callerId,serviceId,methodName);
        return ResponseResultUtil.ofError(ResponseCodeEnum.SERVICE_ERROR.getCode(),ResponseCodeEnum.SERVICE_ERROR.getName());
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    private ResponseResult<ServiceResult> validated(Method serviceMethod, ServiceParam[] param){
        if (param==null||param.length==0 || param[0] == null){
            return null;
        }
        Annotation[][] parameterAnnotions = serviceMethod.getParameterAnnotations();
        if (parameterAnnotions!=null &&
                parameterAnnotions.length>0 &&
                parameterAnnotions[0].length>0 &&
                parameterAnnotions[0][0].annotationType() == Validated.class){
            Validated validated = (Validated)parameterAnnotions[0][0];
            Class<?>[] groups   = validated.value();
            ResponseResult<ServiceResult> result = ValidationUtils.validateEntity(param,groups);

            return result;
        }
        return null;
    }
}
